Lesson 02

Lesson 02

Data types and control flow

Data types and control flow

These primitives are the building blocks for every script and system you’ll write.

These primitives are the building blocks for every script and system you’ll write.

Numbers and strings

Numbers and strings

# Numbers        
# Numbers

tax_rate = 0.12          # floattax_rate = 0.12          # float

units = 7                # intunits = 7                # int

total = units * 9.5total = units * 9.5

with_tax = total * (1 + tax_rate)with_tax = total * (1 + tax_rate)

print(with_tax)print(with_tax)



# Strings# Strings

title = "SigLabs"title = "SigLabs"

tagline = f"Welcome to {title}"tagline = f"Welcome to {title}"

print(tagline.upper())print(tagline.upper())

print("abc".replace("b", "x"))print("abc".replace("b", "x"))

Collections

Collections

# Lists (ordered, mutable)        
# Lists (ordered, mutable)

prices = [10.0, 12.5, 9.9]prices = [10.0, 12.5, 9.9]

prices.append(11.2)prices.append(11.2)



# Tuples (ordered, immutable)# Tuples (ordered, immutable)

point = (3, 4)point = (3, 4)



# Dicts (key-value)# Dicts (key-value)

instrument = {"symbol": "SIG", "price": 12.34}instrument = {"symbol": "SIG", "price": 12.34}

instrument["currency"] = "USD"instrument["currency"] = "USD"



# Sets (unique, unordered)# Sets (unique, unordered)

seen = {"AAPL", "MSFT"}seen = {"AAPL", "MSFT"}

seen.add("GOOG")seen.add("GOOG")

Conditionals

Conditionals

price = 12.5        
price = 12.5

if price > 20:if price > 20:

    label = "expensive"    label = "expensive"

elif price > 10:elif price > 10:

    label = "fair"    label = "fair"

else:else:

    label = "cheap"    label = "cheap"

print(label)print(label)

Loops

Loops

# for-loop        
# for-loop

symbols = ["SIG", "EURUSD", "BTC"]symbols = ["SIG", "EURUSD", "BTC"]

for s in symbols:for s in symbols:

    print(s)    print(s)



# while-loop# while-loop

count = 3count = 3

while count:while count:

    print(count)    print(count)

    count -= 1    count -= 1

Comprehensions

Comprehensions

# List, dict, set comprehensions        
# List, dict, set comprehensions

nums = [1, 2, 3, 4, 5]nums = [1, 2, 3, 4, 5]

squares = [n*n for n in nums]squares = [n*n for n in nums]

evens = {n for n in nums if n % 2 == 0}evens = {n for n in nums if n % 2 == 0}

index = {n: i for i, n in enumerate(nums)}index = {n: i for i, n in enumerate(nums)}

print(squares, evens, index)print(squares, evens, index)

Accelerate with SigLabs Financial Software

Accelerate with SigLabs Financial Software

Forecasting, market screening, risk insights, and reporting—built for speed and clarity.

Forecasting, market screening, risk insights, and reporting—built for speed and clarity.

Next steps

Next steps

You’re ready for functions, modules, and venv in Lesson 03.

You’re ready for functions, modules, and venv in Lesson 03.

cpu_bound_task(COUNT)
cpu_bound_task(COUNT)

Lesson 02

end_seq = time.time()

Data types and control flow

print(f"Sequential execution time: {end_seq - start_seq:.4f} seconds")

These primitives are the building blocks for every script and system you’ll write.

# --- Run with Threads ---

Numbers and strings

thread1 = threading.Thread(target=cpu_bound_task, args=(COUNT,))
# Numbersthread2 = threading.Thread(target=cpu_bound_task, args=(COUNT,))

tax_rate = 0.12          # float

units = 7                # intstart_thread = time.time()

total = units * 9.5thread1.start()

with_tax = total * (1 + tax_rate)thread2.start()

print(with_tax)thread1.join() # Wait for thread 1 to finish

thread2.join() # Wait for thread 2 to finish

# Stringsend_thread = time.time()

title = "SigLabs"print(f"Threaded execution time: {end_thread - start_thread:.4f} seconds")

tagline = f"Welcome to {title}"

print(tagline.upper())# Note: Due to the GIL, the threaded execution time for this CPU-bound task

print("abc".replace("b", "x"))# will likely be similar to or even slightly longer than the sequential time,

# not significantly faster as one might expect from true parallelism.

Collections




            Section 2: Introduction to Cython+

Cython is a superset of Python that allows writing C extensions. It lets you compile Python-like code (with optional static type declarations) into efficient C code. This is particularly useful for speeding up CPU-bound bottlenecks in Python applications or for interfacing directly with C/C++ libraries without writing complex C API code manually.

  

        
# Lists (ordered, mutable)

prices = [10.0, 12.5, 9.9]# --- Example Cython code (save as example.pyx) ---

prices.append(11.2)# This file needs to be compiled using a setup.py script



# Tuples (ordered, immutable)# cdef allows defining C variables for speed

point = (3, 4)# cpdef creates both C and Python accessible versions

# cython: language_level=3 # Specify Python 3 semantics

# Dicts (key-value)

instrument = {"symbol": "SIG", "price": 12.34}cpdef int sum_of_squares_cython(int n):

instrument["currency"] = "USD"    # Declare C integer type for loop variable and result

    cdef int i, total = 0

# Sets (unique, unordered)    for i in range(n):

seen = {"AAPL", "MSFT"}        total += i * i

seen.add("GOOG")    return total

# --- Example setup.py (save as setup.py) ---

Conditionals

# from setuptools import setup
price = 12.5# from Cython.Build import cythonize

if price > 20:#

    label = "expensive"# setup(

elif price > 10:#     ext_modules = cythonize("example.pyx")

    label = "fair"# )

else:# Run: python setup.py build_ext --inplace

    label = "cheap"

print(label)# --- Example Python usage (after compiling) ---

# import example # Import the compiled module # import time

Loops

#
# for-loop# N = 10000

symbols = ["SIG", "EURUSD", "BTC"]#

for s in symbols:# start = time.time()

    print(s)# result = example.sum_of_squares_cython(N)

# end = time.time()

# while-loop# print(f"Cython result: {result}, Time: {end - start:.6f}s")

count = 3#

while count:# # Compare with pure Python (usually slower)

    print(count)# def sum_of_squares_python(n):

    count -= 1#     total = 0

# for i in range(n): # total += i * i

Comprehensions

# return total
# List, dict, set comprehensions#

nums = [1, 2, 3, 4, 5]# start_py = time.time()

squares = [n*n for n in nums]# result_py = sum_of_squares_python(N)

evens = {n for n in nums if n % 2 == 0}# end_py = time.time()

index = {n: i for i, n in enumerate(nums)}# print(f"Python result: {result_py}, Time: {end_py - start_py:.6f}s")

print(squares, evens, index)

# Placeholder message as direct execution isn't possible here: print("Cython code needs compilation. See comments for structure.")

Accelerate with SigLabs Financial Software




            Section 3: CPython Memory Management+

CPython primarily uses reference counting for memory management. Each object keeps track of how many references point to it. When the count drops to zero, the object's memory is deallocated. A cyclic garbage collector runs periodically to detect and break reference cycles (objects referring to each other) that reference counting alone cannot handle.

  

          

Forecasting, market screening, risk insights, and reporting—built for speed and clarity.

import sys
import gc # Garbage Collector interface

Next steps

# --- Reference Counting Example ---

You’re ready for functions, modules, and venv in Lesson 03.

a = [1, 2, 3] # Object [1, 2, 3] created, ref count = 1 (a)
print(f"Initial ref count for a: {sys.getrefcount(a) - 1}") # -1 because getrefcount itself adds a temporary ref ← Prev: Setup Next: Functions, modules, and venv →b = a # Ref count = 2 (a, b)
print(f"Ref count after b = a: {sys.getrefcount(a) - 1}")
del b # Ref count = 1 (a) print(f"Ref count after del b: {sys.getrefcount(a) - 1}") class Node: def __init__(self, name): self.name = name self.neighbor = None print(f"Node '{self.name}' created.") def __del__(self): # This is called when the object is about to be destroyed print(f"Node '{self.name}' being destroyed.") # Create a cycle node1 = Node("A") node2 = Node("B") node1.neighbor = node2 # node1 references node2 node2.neighbor = node1 # node2 references node1 # Check initial reference counts (will be 2 due to self + neighbor) print(f"Ref count node1: {sys.getrefcount(node1) - 1}") print(f"Ref count node2: {sys.getrefcount(node2) - 1}") # Remove external references del node1 del node2 # At this point, the nodes still reference each other (ref count > 0) # They are unreachable but not deallocated by ref counting alone. print("\nNodes deleted from namespace, but cycle exists.") print("Running garbage collection manually...") collected_count = gc.collect() # Manually run the cyclic GC print(f"Garbage collector collected {collected_count} objects.") # Now the __del__ methods should be called as the GC breaks the cycle.


            Section 4: Abstract Base Classes (ABCs)+

Abstract Base Classes (ABCs) from the `abc` module define interfaces or contracts that subclasses must implement. They use the `@abstractmethod` decorator to specify methods that must be overridden. This enforces a common structure across different implementations, promoting better code organization and polymorphism.

  

from abc import ABC, abstractmethod

# Define an Abstract Base Class
class Vehicle(ABC):
    @abstractmethod
    def start_engine(self):
        """Abstract method to start the vehicle's engine."""
        pass

    @abstractmethod
    def stop_engine(self):
        """Abstract method to stop the vehicle's engine."""
        pass

    def honk(self):
        """A concrete method available to all subclasses."""
        print("Generic honk!")

# Concrete subclass implementing the abstract methods
class Car(Vehicle):
    def start_engine(self):
        print("Car engine started with a key turn.")

    def stop_engine(self):
        print("Car engine stopped.")

    # Can override concrete methods too
    def honk(self):
        print("Beep beep!")

# Another concrete subclass
class Motorcycle(Vehicle):
    def start_engine(self):
        print("Motorcycle engine started with a kick.")

    def stop_engine(self):
        print("Motorcycle engine stopped.")

# Usage
my_car = Car()
my_moto = Motorcycle()

vehicles = [my_car, my_moto]

for v in vehicles:
    v.start_engine()
    v.honk()
    v.stop_engine()
    print("-" * 10)

# Attempting to instantiate the ABC directly will fail
try:
    generic_vehicle = Vehicle()
except TypeError as e:
    print(f"Error trying to instantiate ABC: {e}")

# Attempting to instantiate a subclass that *doesn't* implement
# all abstract methods will also fail:
# class BrokenCar(Vehicle):
#     def start_engine(self): pass
# try:
#    bad_car = BrokenCar() # Raises TypeError
# except TypeError as e:
#    print(f"Error with incomplete subclass: {e}")




            Section 5: Threading vs Multiprocessing+

Python's `threading` module allows concurrent execution within a single process, sharing memory but limited by the GIL for CPU-bound tasks. The `multiprocessing` module bypasses the GIL by creating separate processes, each with its own memory space, enabling true parallelism for CPU-bound work but incurring higher communication overhead.

  

import time
import threading
import multiprocessing
import os

# Shared function (can be CPU or I/O bound depending on content)
def worker_function(worker_id, duration):
    pid = os.getpid()
    thread_id = threading.get_ident()
    print(f"Worker {worker_id}: START - PID {pid}, Thread {thread_id}")
    time.sleep(duration) # Simulate work (I/O bound)
    # For CPU-bound, replace sleep with heavy computation
    print(f"Worker {worker_id}: END   - PID {pid}, Thread {thread_id}")
    return f"Result from worker {worker_id}"

NUM_WORKERS = 3
DURATION = 1

# --- Using Threading ---
print("--- Starting with Threading ---")
threads = []
start_th = time.time()
for i in range(NUM_WORKERS):
    t = threading.Thread(target=worker_function, args=(f"T{i}", DURATION))
    threads.append(t)
    t.start()

for t in threads:
    t.join() # Wait for threads to complete
end_th = time.time()
print(f"Threading finished in {end_th - start_th:.4f}s\n")
# Note: All threads run within the same PID.

# --- Using Multiprocessing ---
# Required for multiprocessing on some platforms (like Windows)
if __name__ == "__main__": # Guard clause
    print("--- Starting with Multiprocessing ---")
    processes = []
    start_mp = time.time()
    # Use a Pool for easier management (optional)
    # with multiprocessing.Pool(processes=NUM_WORKERS) as pool:
    #     results = pool.starmap(worker_function, [(f"P{i}", DURATION) for i in range(NUM_WORKERS)])

    # Manual process creation:
    for i in range(NUM_WORKERS):
        p = multiprocessing.Process(target=worker_function, args=(f"P{i}", DURATION))
        processes.append(p)
        p.start()

    results = [] # Placeholder if not using Pool.join() implicitly handles results
    for p in processes:
        p.join() # Wait for processes to complete
        # results.append(p.exitcode) # Can get exit code

    end_mp = time.time()
    print(f"Multiprocessing finished in {end_mp - start_mp:.4f}s")
    # Note: Each process will have a different PID.

# Key takeaway: For I/O-bound tasks (like time.sleep), both can offer concurrency.
# For CPU-bound tasks, multiprocessing typically offers true parallelism (speedup),
# while threading is limited by the GIL in CPython.




            Section 6: Functional Programming Tools (`functools`)+

Python supports functional programming paradigms. The `functools` module provides higher-order functions and operations on callable objects. Key tools include `partial` for freezing function arguments, `reduce` for cumulative operations, `lru_cache` for memoization, and `wraps` for creating well-behaved decorators.

  

import functools
import time
import operator

# --- functools.partial ---
def power(base, exponent):
    return base ** exponent

# Create specialized functions by freezing arguments
square = functools.partial(power, exponent=2)
cube = functools.partial(power, exponent=3)

print(f"Square of 5: {square(5)}") # Output: 25
print(f"Cube of 3: {cube(3)}")     # Output: 27

# --- functools.reduce ---
# Performs cumulative operations on a sequence
numbers = [1, 2, 3, 4, 5]
product = functools.reduce(operator.mul, numbers) # Multiply all numbers
# Equivalent to (((1*2)*3)*4)*5
print(f"Product of {numbers}: {product}") # Output: 120

# --- functools.lru_cache ---
# Memoization decorator - caches results of expensive function calls
@functools.lru_cache(maxsize=128) # Least Recently Used cache
def slow_fibonacci(n):
    """Calculates Fibonacci recursively (inefficient without cache)."""
    if n < 2:
        return n
    # print(f"Calculating fib({n})") # Uncomment to see cache effect
    time.sleep(0.01) # Simulate slowness
    return slow_fibonacci(n-1) + slow_fibonacci(n-2)

start_cache = time.time()
result_fib = slow_fibonacci(15) # Calculation happens
end_cache = time.time()
print(f"Fib(15) = {result_fib}. First call time: {end_cache - start_cache:.4f}s")

start_cache2 = time.time()
result_fib2 = slow_fibonacci(15) # Result retrieved from cache instantly
end_cache2 = time.time()
print(f"Fib(15) = {result_fib2}. Second call time: {end_cache2 - start_cache2:.4f}s")

# --- functools.wraps ---
# (Already demonstrated in Section 2: Advanced Decorators)
# Ensures decorators preserve metadata of the original function.



Grab the great 7-zip compression program from here: 7-zip



Discover Comprehensive Tutorials & Software Downloads

Unlock a wealth of tutorials and software downloads available across our platform.

Image descriptionStart Exploring
 Welcome to your all-in-one destination in the Philippines! Settle into our welcoming accommodations, then treat your taste buds at our restaurant, featuring both elegant fine dining and authentic local cuisine, including special 8-course meals. Explore the islands with our tourist tours and car rentals, or let our shuttle service handle your transport. Find unique treasures in our handcrafted clothing line, unwind with yoga and massage, or catch some waves with our surfboard rentals. Join us at the bar, order takeaway, or book us for your next celebration or corporate event – we even offer magical beach dining setups. We also have a selection of reads and software in our bookstore and software store.
--> Python 02 — Data Types and Control Flow | SigLabs Code

Lesson 02

Data types and control flow

These primitives are the building blocks for every script and system you’ll write.

Numbers and strings

# Numbers
tax_rate = 0.12          # float
units = 7                # int
total = units * 9.5
with_tax = total * (1 + tax_rate)
print(with_tax)

# Strings
title = "SigLabs"
tagline = f"Welcome to {title}"
print(tagline.upper())
print("abc".replace("b", "x"))

Collections

# Lists (ordered, mutable)
prices = [10.0, 12.5, 9.9]
prices.append(11.2)

# Tuples (ordered, immutable)
point = (3, 4)

# Dicts (key-value)
instrument = {"symbol": "SIG", "price": 12.34}
instrument["currency"] = "USD"

# Sets (unique, unordered)
seen = {"AAPL", "MSFT"}
seen.add("GOOG")

Conditionals

price = 12.5
if price > 20:
        label = "expensive"
elif price > 10:
        label = "fair"
else:
        label = "cheap"
print(label)

Loops

# for-loop
symbols = ["SIG", "EURUSD", "BTC"]
for s in symbols:
        print(s)

# while-loop
count = 3
while count:
        print(count)
        count -= 1

Comprehensions

# List, dict, set comprehensions
nums = [1, 2, 3, 4, 5]
squares = [n*n for n in nums]
evens = {n for n in nums if n % 2 == 0}
index = {n: i for i, n in enumerate(nums)}
print(squares, evens, index)

Accelerate with SigLabs Financial Software

Forecasting, market screening, risk insights, and reporting—built for speed and clarity.

Next steps

You’re ready for functions and modules in Lesson 03.